GPIO 使用
在 ESP32 中,GPIO 的作用
GPIO 全称是 "General Purpose Input/Output"(通用输入输出),可以理解为芯片上的"数字引脚"。就像你在软件开发中操作变量一样,GPIO 让你可以操作硬件引脚,控制电平的高低(类似 true/false)。
ESP32 芯片上带有几十个引脚(不同封装型号数量可能不同),其中大多数都可以配置为 GPIO。每个 GPIO 引脚都可以通过软件设定为 输入 或 输出:
-
输入模式
可以用来读取外部信号(例如按钮、传感器的数据电平)。
ESP32 GPIO 还支持上拉、下拉电阻配置,方便读取稳定的逻辑电平。 -
输出模式
可以用来驱动外部器件(例如点亮 LED、控制继电器、与外设通信)。 -
多功能复用
ESP32 的 GPIO 大部分都可以复用为其他外设功能(UART、I2C、SPI、PWM、ADC、DAC 等),不仅仅是单纯的“高低电平”。
ESP32 GPIO 的一些特点
数量:ESP32 通常提供约 34 个可用的 GPIO (不过并不是每一个封装都会引出全部引脚)。
电压:GPIO 工作在 3.3V 电平,输入电压必须在 0~3.3V 之间,高于这个范围可能烧坏芯片。
- 特殊功能引脚:
- GPIO6 ~ GPIO11:通常用于连接 Flash,不建议用户使用。
- GPIO34 ~ GPIO39:只支持输入,不支持输出。
- 可配置模式:通过软件 API(例如
pinMode()在 Arduino 框架下,或 ESP-IDF 的gpio_set_direction())来配置。
简单例子(Arduino 框架下)
int ledPin = 2; // ESP32 开发板上的一般 IO2 引脚接了板载 LED
void setup() {
pinMode(ledPin, OUTPUT); // 设置 GPIO2 为输出
}
void loop() {
digitalWrite(ledPin, HIGH); // 输出高电平,点亮 LED
delay(1000);
digitalWrite(ledPin, LOW); // 输出低电平,熄灭 LED
delay(1000);
}
上拉(Pull-up)和下拉(Pull-down)是什么?
当 GPIO 被配置为 输入 时,它需要去“读取”一个电平(高电平=1,低电平=0)。
但是有时候输入引脚并没有可靠的电压信号接上去(比如一个按钮没按下时,电路是悬空的),此时引脚电压就会处在“不确定状态”(可能受环境杂讯影响),这就会导致读到的数据不稳定。
为了解决这个问题,芯片内部提供了 上拉电阻(pull-up resistor) 和 下拉电阻(pull-down resistor):
-
上拉(Pull-up):给 GPIO 接一个电阻连到 VCC (3.3V),使得当信号未被外部主动驱动时,GPIO 默认处于 高电平。
-
下拉(Pull-down):给 GPIO 接一个电阻连到 GND (0V),使得当信号未被外部主动驱动时,GPIO 默认处于 低电平。
ESP32 自带了这些上拉/下拉电阻,可以通过软件配置打开,非常方便。
假设你有一个按钮,想用 GPIO 读取按钮状态:
方案1:上拉模式
- 按钮一端接 GND,另一端接 GPIO。
- 把 GPIO 配置为 输入 + 内部上拉。
- 没按下按钮时 → 电路开路,GPIO 通过内部上拉电阻 = 高电平 → 读到
1。 - 按下按钮时 → GPIO 直接接 GND → 低电平 → 读到
0。
方案2:下拉模式
- 按钮一端接 VCC,另一端接 GPIO。
- 把 GPIO 配置为 输入 + 内部下拉。
- 没按按钮时 → GPIO 通过内部下拉电阻接地 = 低电平 → 读到
0。 - 按下按钮时 → GPIO 被拉到 VCC → 高电平 → 读到
1。
int buttonPin = 4;
void setup() {
pinMode(buttonPin, INPUT_PULLUP); // 使用内部上拉
Serial.begin(115200);
}
void loop() {
int state = digitalRead(buttonPin);
if (state == LOW) { // 按钮按下
Serial.println("Button Pressed");
} else {
Serial.println("Button Released");
}
delay(200);
}
这里用的是 上拉模式,所以当按钮按下时,读取到的是 低电平。
gpio_config_t 配置
在 ESP-IDF 中,gpio_config_t 是一个结构体,用来集中描述 GPIO 的配置选项,然后通过 gpio_config() 函数一次性完成设置。
#include "driver/gpio.h"
2. 结构体定义
精简版(不同 IDF 版本可能略有差别,但核心一致):
typedef struct {
// GPIO 引脚掩码(哪些引脚要配置)
uint64_t pin_bit_mask;
// GPIO 模式
gpio_mode_t mode;
// 上拉功能(0=关闭,1=开启)
gpio_pullup_t pull_up_en;
// 下拉功能(0=关闭,1=开启)
gpio_pulldown_t pull_down_en;
// 中断类型(例如上升沿触发、下降沿触发)
gpio_int_type_t intr_type;
} gpio_config_t;
3. 各字段解释
pin_bit_mask
- 采用 位掩码(bit mask)来选择要配置的 GPIO。
- 比如
1ULL << GPIO_NUM_4表示选择 GPIO4。 - 也可以一次配置多个,比如:
cfg.pin_bit_mask = (1ULL << GPIO_NUM_4) | (1ULL << GPIO_NUM_5);
这里之所以使用掩码是因为可以很方便的选择多个位
mode
- 引脚的工作模式,类型是
gpio_mode_t:GPIO_MODE_DISABLE→ 不用该引脚。GPIO_MODE_INPUT→ 输入模式。GPIO_MODE_OUTPUT→ 输出模式。GPIO_MODE_OUTPUT_OD→ 开漏输出。GPIO_MODE_INPUT_OUTPUT→ 输入输出都可。GPIO_MODE_INPUT_OUTPUT_OD→ 可输入+开漏输出。
pull_up_en
- 是否启用上拉电阻(
GPIO_PULLUP_ENABLE/GPIO_PULLUP_DISABLE)。
pull_down_en
- 是否启用下拉电阻(
GPIO_PULLDOWN_ENABLE/GPIO_PULLDOWN_DISABLE)。
intr_type
- 中断触发类型,类型是
gpio_int_type_t:GPIO_INTR_DISABLE→ 不使用中断。GPIO_INTR_POSEDGE→ 上升沿触发。GPIO_INTR_NEGEDGE→ 下降沿触发。GPIO_INTR_ANYEDGE→ 任意边沿触发。GPIO_INTR_LOW_LEVEL→ 低电平触发。GPIO_INTR_HIGH_LEVEL→ 高电平触发。
输入 + 中断(例如按钮)
#include "driver/gpio.h"
void app_main(void)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_NEGEDGE; // 按钮按下 → 下降沿触发中断
io_conf.mode = GPIO_MODE_INPUT; // 输入模式
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_4); // 选择 GPIO4
io_conf.pull_up_en = GPIO_PULLUP_ENABLE; // 启用上拉
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // 禁用下拉
gpio_config(&io_conf); // 应用配置
// 然后你可以注册 ISR 回调处理按钮事件
}
输出(例 如点亮 LED)
void app_main(void)
{
gpio_config_t io_conf = {};
io_conf.intr_type = GPIO_INTR_DISABLE; // 不用中断
io_conf.mode = GPIO_MODE_OUTPUT; // 输出模式
io_conf.pin_bit_mask = (1ULL << GPIO_NUM_2); // 选择 GPIO2
io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // 不启用上拉
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // 不启用下拉
gpio_config(&io_conf);
// 控制 GPIO2
gpio_set_level(GPIO_NUM_2, 1); // 输出高电平 → LED 亮
gpio_set_level(GPIO_NUM_2, 0); // 输出低电平 → LED 灭
}